#include <wfcClient.h>
#include <wfcSession.h>
#include <wxyutils.h>
#include <wxyDrawing.h>
#include <wxyAutoIndex.h>
#include <OTKXmath.h>
#include <algorithm>
#include "main.h"
#include <regex>
#include <typeinfo>

wfcStatus AutoIndex()
{
    //检查当前环境
    if (CheckEnv(1, pfcMDL_DRAWING) == 0)
    {
        return wfcTK_NO_ERROR;
    }
    pfcPoint3D_ptr loc;
    try
    {
        pfcSession_ptr session = pfcGetProESession();
        pfcModel_ptr mdl = session->GetCurrentModel();
        pfcDrawing_ptr drawing = pfcDrawing::cast(mdl);
        //创建尺寸列表容器
        vector<DimensionStruct> dimlist;
        CollectDimension(mdl, dimlist);
        SortDimensions(dimlist);
        vector<DimensionStruct>::iterator pr;
        pfcDetailSymbolInstInstructions_ptr symins = CreateSymbolInstInstruction(mdl);
        int listsize = dimlist.size();
        if (dimlist.empty())
        {
            session->UIDisplayLocalizedMessage(ERRFILE, "Dimension is not found!", NULL);
            return wfcTK_NO_ERROR;
        }
        try
        {
            for (xint i = 0; i < listsize; i++)
            {
                DimensionStruct dimstruct = dimlist[i];
                pfcDimension2D_ptr dimension = dimstruct.GetDimension();
                CreateSymbolIndex(drawing, dimlist[i], symins, i);
            }
            drawing->Regenerate();
        }
        OTK_EXCEPTION_PRINT_LOG
        catch (exception e)
        {
            LOG(LOG_ERROR) << "CreateSymbolIndex Failed! Error is " << e.what();
        }
        //销毁dimlist
        dimlist.clear();
        return wfcTK_NO_ERROR;
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (exception e)
    {
        LOG(LOG_ERROR) << "Autoindex Failed! Error is " << e.what();
    }
    return wfcTK_NO_ERROR;
}
wfcStatus CleanIndex()
{
    if (CheckEnv(1, pfcMDL_DRAWING) == 0)
    {
        return wfcTK_NO_ERROR;
    }
    try
    {
        pfcSession_ptr session = pfcGetProESession();
        wfcWSession_ptr wsession = wfcWSession::cast(pfcGetProESession());
        pfcModel_ptr mdl = session->GetCurrentModel();
        pfcDetailItems_ptr items;
        pfcModel2D_ptr mdl2d = pfcModel2D::cast(mdl);
        if (!CollectSymbols(items))
            return wfcTK_NO_ERROR;
        xint numitems = items->getarraysize();
        for (xint i = 0; i < numitems; i++)
        {
            pfcDetailItem_ptr detailitem = items->get(i);
            pfcDetailSymbolInstItem_ptr syminst = pfcDetailSymbolInstItem::cast(detailitem);
            pfcDetailSymbolInstInstructions_ptr syminstrut = syminst->GetInstructions(true);
            pfcDetailSymbolDefItem_ptr defsym = syminstrut->GetSymbolDef();
            pfcDetailSymbolDefInstructions_ptr defsymins = defsym->GetInstructions();
            xstring symname = defsymins->GetName();
            if (symname == "BOOLEAN")
            {
                detailitem->Delete();
            }
        }
        mdl2d->Regenerate();
        return wfcTK_NO_ERROR;
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (exception e)
    {
        LOG(LOG_ERROR) << "Clean Index Failed! Error is " << e.what();
    }
    return wfcTK_NO_ERROR;
}
//收集尺寸 放入链表
void CollectDimension(pfcModel_ptr mdl, vector<DimensionStruct> &Dimlist)
{
    try
    {
        pfcPoint3D_ptr loc;
        pfcDrawing_ptr drawing = pfcDrawing::cast(mdl);
        pfcDimension2Ds_ptr Dimensions = pfcDimension2Ds::create();
        pfcModels_ptr Models = drawing->ListModels();
        //获取组件图中的尺寸
        for (xint i = 0; i < Models->getarraysize(); i++)
        {
            Dimensions->insertseq(0, drawing->ListShownDimensions(Models->get(i), pfcModelItemType_nil));
        }
        //获取当前模型中尺寸
        pfcModelItems_ptr mdlitems = mdl->ListItems(pfcITEM_DIMENSION);
        xint numitems = mdlitems->getarraysize();
        for (xint i = 0; i < numitems; i++)
        {
            pfcModelItem_ptr mdlitem = mdlitems->get(i);
            pfcDimension2D_ptr dim = pfcDimension2D::cast(mdlitem);
            Dimensions->append(dim);
        }
        //根据Dimensions创建DimStruct对象并放入vector，便于后续操作
        for (xint i = 0; i < Dimensions->getarraysize(); i++)
        {
            pfcDimension2D_ptr dim = Dimensions->get(i);
            DimensionStruct DimStruct;
            //DimStruct成员变量在初始化时创建
            if (DimStruct.Create(dim))
                Dimlist.push_back(DimStruct);
        }
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (...)
    {
        LOG(LOG_ERROR) << "Collect Dimension Failed!";
    }
}

//尺寸位置排序函数
void SortDimensions(vector<DimensionStruct> &dims)
{
    sort(dims.begin(), dims.end(), SortByView);
    //按视图分组，按角度排序
    try
    {
        vector<DimensionStruct>::iterator it = dims.begin();
        while (it < dims.end())
        {
            vector<DimensionStruct>::iterator p = it + 1;
            while (p < dims.end())
            {
                if (p->GetViewName() == it->GetViewName())
                    p++;
                else
                    break;
            }
            if (p - it > 1)
                sort(it, p, SortByAngle);
            it = p;
        }
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (...)
    {
        LOG(LOG_ERROR) << "SortByAngle Failed!";
    }
    //按角度分组，按距离排序
    try
    {
        vector<DimensionStruct>::iterator it2 = dims.begin();
        while (it2 != dims.end())
        {
            vector<DimensionStruct>::iterator p = it2 + 1;
            double AngleBond = 5;
            double BondRadio = 5;
            while (p < dims.end())
            {
                //动态扩展边界，降低相邻尺寸序号相差较大的概率
                if (p->GetViewName() == it2->GetViewName() && p->GetAngle() - it2->GetAngle() < AngleBond)
                {
                    p++;
                }
                else if (p->GetViewName() == it2->GetViewName() && p->GetAngle() - it2->GetAngle() < (AngleBond + BondRadio))
                {
                    AngleBond += BondRadio;
                }
                else
                    break;
            }
            if (p - it2 > 1)
            {
                sort(it2, p, SortByDistance);
                //dims.begin没有前一节点，后面计算距离越界
                if (it2 != dims.begin())
                {
                    double d1, d2;
                    //计算当前片段起点和终点与片段前一点距离，如果终点距离较近，则将片段逆序
                    d1 = Cal_Distance(it2->GetSymLoc(), (it2 - 1)->GetSymLoc());
                    d2 = Cal_Distance((p - 1)->GetSymLoc(), (it2 - 1)->GetSymLoc());
                    if (d2 < d1)
                    {
                        reverse(it2, p);
                    }
                }
            }
            it2 = p;
        }
    }

    OTK_EXCEPTION_PRINT_LOG
    catch (...)
    {
        LOG(LOG_ERROR) << "SortByDistance Failed!";
    }
}
bool SortByLoc(const DimensionStruct &td1, const DimensionStruct &td2)
{
    DimensionStruct d1, d2;
    d1 = td1;
    d2 = td2;
    float vcx1, vcx2, vcy1, vcy2;
    vcx1 = d1.GetViewCenter()->get(0);
    vcx2 = d2.GetViewCenter()->get(0);
    vcy1 = d1.GetViewCenter()->get(1);
    vcy2 = d2.GetViewCenter()->get(1);
    if (vcx1 > vcx2)
    {
        return false; //视图中心：d1在d2的右侧
    }
    else if (IsEqual(vcx1, vcx2) && (vcy1 < vcy2))
    {
        return false; //试图中心：d1在d2的下方
    }
    else if (IsEqual(vcx1, vcx2) && IsEqual(vcy1, vcy2))
    {
        double angle1, angle2;
        double dist1, dist2;
        angle1 = d1.GetAngle();
        angle2 = d2.GetAngle();
        dist1 = d1.GetDistance();
        dist2 = d2.GetDistance();
        if (angle1 > angle2)
        {
            return false; //d1在d2顺时针的前方
        }
        else if (IsEqual(angle1, angle2) && dist1 > dist2)
        {
            return false; //d1比d2靠近视图中心
        }
    }
    return true;
}
//尺寸位置排序函数 按视图排序
bool SortByView(const DimensionStruct &td1, const DimensionStruct &td2)
{
    DimensionStruct d1, d2;
    d1 = td1;
    d2 = td2;
    float vcx1, vcx2, vcy1, vcy2;
    vcx1 = d1.GetViewCenter()->get(0);
    vcx2 = d2.GetViewCenter()->get(0);
    vcy1 = d1.GetViewCenter()->get(1);
    vcy2 = d2.GetViewCenter()->get(1);
    if (vcx1 > vcx2)
    {
        return false; //视图中心：d1在d2的右侧
    }
    else if (IsEqual(vcx1, vcx2) && (vcy1 < vcy2))
    {
        return false; //视图中心：d1在d2的下方
    }
    return true;
}
//尺寸位置排序函数 按角度排序
bool SortByAngle(const DimensionStruct &td1, const DimensionStruct &td2)
{
    DimensionStruct d1, d2;
    d1 = td1;
    d2 = td2;
    double angle1, angle2;
    angle1 = d1.GetAngle();
    angle2 = d2.GetAngle();
    if (d1.GetViewName() == d2.GetViewName() && angle1 > angle2)
    {
        return false; //d1在d2顺时针的前方
    }
    return true;
}
//尺寸位置排序函数 按距离排序
bool SortByDistance(const DimensionStruct &td1, const DimensionStruct &td2)
{
    DimensionStruct d1, d2;
    d1 = td1;
    d2 = td2;
    double dist1, dist2;
    dist1 = d1.GetDistance();
    dist2 = d2.GetDistance();
    if (dist1 > dist2)
    {
        return false; //d1比d2靠近视图中心
    }
    return true;
}
//创建球标
void CreateSymbolIndex(pfcDrawing_ptr drawing,
                       DimensionStruct dim,
                       pfcDetailSymbolInstInstructions_ptr symins,
                       xint index)
{
    try
    {
        pfcPoint3D_ptr symloc = dim.GetSymLoc();
        // symloc->set(0, symloc->get(0));
        // symloc->set(1, symloc->get(1));
        stringstream symindex;
        symindex << index + 1;
        pfcDetailVariantText_ptr textvalue = pfcDetailVariantText::Create("index", symindex);
        pfcDetailVariantTexts_ptr textvalues = pfcDetailVariantTexts::create();
        textvalues->append(textvalue);
        symins->SetTextValues(textvalues);
        // pfcSymbolDefAttachment_ptr defattachment = pfcSymbolDefAttachment::Create(pfcSYMDEFATTACH_FREE, symloc);
        // symins->SetDefAttachment(defattachment);
        pfcSelection_ptr selection = pfcCreateModelItemSelection((pfcModelItem_ptr)dim.GetDimension());
        pfcOffsetAttachment_ptr attachment = pfcOffsetAttachment::Create(selection, symloc); //与当前尺寸关联
        pfcDetailLeaders_ptr leaders = pfcDetailLeaders::Create();
        leaders->SetItemAttachment((pfcAttachment_ptr)attachment);
        symins->SetInstAttachment(leaders);
        symins->SetIsDisplayed(true);
        symins->SetScaledHeight(3);
        ((pfcDetailItemOwner_ptr)drawing)->CreateDetailItem((pfcDetailCreateInstructions_ptr)symins);
        textvalues->clear(); //不清空会有异常
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (...)
    {
        LOG(LOG_ERROR) << "Create Symbol Index Error!";
    }
}
//计算球标位置
pfcPoint3D_ptr CalSymbolLoc(pfcDimension2D_ptr dimension)
{
    pfcPoint3D_ptr loc;
    int DimWidth = CalDimensionValueWideth(dimension);
    //偏移位置补偿 计算的字符宽度加1.5pad间隙/2即为球标到尺寸中心距离
    int tpad = ceil((DimWidth + 1.5 * pad) / 2);
    try
    {
        loc = dimension->GetLocation();
        pfcPoint3Ds_ptr Points = pfcPoint3Ds::create();
        //模型注释中的尺寸GetAttachmentPoints()抛出异常，无法获取，无法依据Attachment Points判断尺寸方向
        if ((dimension->GetDimType() == pfcDIM_LINEAR) && SetDimensionMid(dimension, Points))
        {

            pfcView2D_ptr view = dimension->GetView();
            xreal viewscal = view->GetScale();
            xreal dimvalue, dx, dy;
            dimvalue = dimension->GetDimValue();
            dx = abs(Points->get(0)->get(0) - Points->get(1)->get(0));
            dy = abs(Points->get(0)->get(1) - Points->get(1)->get(1));
            loc = dimension->GetLocation();
            if (IsEqual(dimvalue * viewscal, dy)) //连接点纵坐标差值与尺寸数值相等，为竖直尺寸
            {
                loc->set(0, loc->get(0) - 2);
                loc->set(1, loc->get(1) + tpad);
            }
            else if (IsEqual(dimvalue * viewscal, dx)) //连接点横坐标差值与尺寸数值相等，为水平尺寸
            {
                loc->set(1, loc->get(1) + 2);
                loc->set(0, loc->get(0) + tpad);
            }
            else //斜向尺寸 目前没有方法计算尺寸方向 球标放置在尺寸左上角
            {
                loc->set(0, loc->get(0) - pad);
                loc->set(1, loc->get(1) + pad);
            }
        }
        else if (dimension->GetDimType() == pfcDIM_DIAMETER) //直径尺寸标注文本为水平方向
        {
            loc->set(1, loc->get(1) + 2);
            loc->set(0, loc->get(0) + tpad);
        }
        else //其余尺寸球标放置在左上角
        {
            loc->set(0, loc->get(0) - pad);
            loc->set(1, loc->get(1) + pad);
        }
    }
    OTK_EXCEPTION_PRINT_LOG
    return loc;
}

//计算尺寸字符占位宽度
int CalDimensionValueWideth(pfcDimension2D_ptr dimension)
{
    int tpad = 0;
    try
    {
        smatch result;
        regex pattern("\\d+:");
        xstringsequence_ptr texts = dimension->GetTexts();
        //判断是否有公差
        xbool TolIsDisplay = dimension->GetIsToleranceDisplayed();
        if (TolIsDisplay)
        {
            tpad = 2 * pad;
        }
        //判断是否存在符号
        for (xint j = 0; j < texts->getarraysize(); j++)
        {
            string text = texts->get(j); //{0:⌀}{1:@D}
            string::const_iterator iterStart = text.begin();
            string::const_iterator iterEnd = text.end();
            int temp = 0;
            //正则匹配\d+:的数量
            while (regex_search(iterStart, iterEnd, result, pattern))
            {
                temp += 1;
                iterStart = result[0].second;
            }
            if (temp > 1)
                tpad += (temp - 1) * pad;
        }
        xreal DimValue = dimension->GetDimValue();
        if (DimValue < 1.0)
            DimValue += 1.0;
        int int_dimvalue = round(DimValue * 100); //保留两位小数
        //判断数值占位宽度
        for (int t = 0; t < 2; t++) //处理小数部分
        {
            if (int_dimvalue % 10 != 0)
            {
                tpad += 0.5 * pad; //小数点占位
                break;
            }
            int_dimvalue /= 10;
        }
        while (int_dimvalue != 0) //处理整数部分
        {
            int_dimvalue /= 10;
            tpad += 0.5 * pad;
        }
    }
    OTK_EXCEPTION_PRINT_LOG
    return tpad;
}
//线性尺寸居中 仅能计算水平和竖直尺寸
int SetDimensionMid(pfcDimension2D_ptr dimension, pfcPoint3Ds_ptr &Points)
{
    pfcSession_ptr session = pfcGetProESession();
    pfcModel_ptr model = session->GetCurrentModel();
    pfcDrawing_ptr drawing = pfcDrawing::cast(model);
    try
    {
        if ((dimension->GetDimType() == pfcDIM_LINEAR))
        {
            xint PointsNum = -1;
            pfcSelections_ptr AttachmentPoints;
            AttachmentPoints = dimension->GetAttachmentPoints();
            PointsNum = AttachmentPoints->getarraysize();
            pfcView2D_ptr view = dimension->GetView();
            xreal viewscal = view->GetScale();
            pfcTransform3D_ptr ViewTrans = view->GetTransform();
            pfcDetailItem_ptr DetailItem;
            xint sheetNumber = drawing->GetCurrentSheetNumber();
            pfcTransform3D_ptr SheetTrans;
            SheetTrans = drawing->GetSheetTransform(sheetNumber); //用之前计算，防止后面求逆后出错；后面循环中不存在求逆操作
            for (int a = 0; a < 2; a++)
            {
                pfcPoint3D_ptr tmp_point = AttachmentPoints->get(a)->GetPoint(); //连接点坐标
                pfcModel_ptr AttachModel = AttachmentPoints->get(a)->GetSelModel();
                pfcComponentPath_ptr ComponentPath = AttachmentPoints->get(a)->GetPath();

                //装配图 连接点坐标需从零件转到装配坐标，此处可能存在bug
                if (ComponentPath)
                {
                    pfcTransform3D_ptr ComponentTrans = ComponentPath->GetTransform(false);
                    tmp_point = ComponentTrans->TransformPoint(tmp_point);
                }

                if (AttachModel->GetType() != pfcMDL_DRAWING)
                {
                    tmp_point = ViewTrans->TransformPoint(tmp_point); //连接点为模型中元素时，需要根据视图变换矩阵转换坐标
                }
                tmp_point = SheetTrans->TransformPoint(tmp_point); //屏幕坐标和图框内坐标的坐标转换

                Points->append(tmp_point);
            }
            xreal dimvalue, dx, dy;
            dimvalue = dimension->GetDimValue();
            dx = abs(Points->get(0)->get(0) - Points->get(1)->get(0));
            dy = abs(Points->get(0)->get(1) - Points->get(1)->get(1));
            _direction flag = Direction_nil;
            if (IsEqual(dimvalue * viewscal, dy))
            {
                flag = Vertical;
            }
            else if (IsEqual(dimvalue * viewscal, dx))
            {
                flag = Horizon;
            }
            //调整水平或竖直尺寸居中
            if (flag != -1)
            {
                //此处屏幕坐标和图纸坐标的变换没有搞清楚
                SheetTrans = drawing->GetSheetTransform(sheetNumber); //作用域控制在当前语句块范围内
                SheetTrans->Invert();
                pfcPoint3D_ptr p1, p2, loc;
                p1 = SheetTrans->TransformPoint(Points->get(0));
                p2 = SheetTrans->TransformPoint(Points->get(1));
                loc = dimension->GetLocation();
                //尺寸在尺寸界限之间 将尺寸居中放置
                if ((p1->get(flag) - loc->get(flag)) * (p2->get(flag) - loc->get(flag)) < 0)
                {
                    loc->set(flag, (p1->get(flag) + p2->get(flag)) / 2);
                    ChangeDimensionLocation(dimension, loc);
                    loc = dimension->GetLocation();
                }
            }
        }
        return 1;
    }
    OTK_EXCEPTION_PRINT_LOG
    return 0;
}
//收集球标
int CollectSymbols(pfcDetailItems_ptr &DetailItems)
{
    if (CheckEnv(0) == 0) //默认检查是否为绘图环境
    {
        return wfcTK_NO_ERROR;
    }
    try
    {
        pfcSession_ptr session = pfcGetProESession();
        wfcWSession_ptr wsession = wfcWSession::cast(pfcGetProESession());
        pfcModel_ptr mdl = session->GetCurrentModel();
        pfcModel2D_ptr mdl2d = pfcModel2D::cast(mdl);
        DetailItems = mdl2d->ListDetailItems(pfcDETAIL_SYM_INSTANCE, NULL);
        if (DetailItems == NULL)
        {
            session->UIDisplayLocalizedMessage(ERRFILE, "Not Find Boolean", NULL);
            return wfcTK_NO_ERROR;
        }
        else
            return 1;
    }
    OTK_EXCEPTION_PRINT_LOG
    catch (...)
    {
        LOG(LOG_ERROR) << "Collect Symbols Failed!";
    }
    return wfcTK_NO_ERROR;
}